Conditions | 43 |
Paths | 13464 |
Total Lines | 207 |
Lines | 0 |
Ratio | 0 % |
Changes | 3 | ||
Bugs | 1 | Features | 0 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
Complex classes like wallet_sweeper.js ➔ WalletSweeper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
1 | var UnspentOutputFinder = require('./unspent_output_finder'); |
||
20 | var WalletSweeper = function(backupData, bitcoinDataClient, options) { |
||
21 | /* jshint -W071, -W074 */ |
||
22 | var self = this; |
||
23 | this.defaultSettings = { |
||
24 | network: 'btc', |
||
25 | testnet: false, |
||
26 | regtest: false, |
||
27 | logging: false, |
||
28 | bitcoinCash: false, |
||
29 | sweepBatchSize: 200 |
||
30 | }; |
||
31 | this.settings = _.merge({}, this.defaultSettings, options); |
||
32 | this.bitcoinDataClient = bitcoinDataClient; |
||
33 | this.utxoFinder = new UnspentOutputFinder(bitcoinDataClient, this.settings); |
||
34 | this.sweepData = null; |
||
35 | |||
36 | // set the bitcoinlib network |
||
37 | if (typeof options.network === "object") { |
||
38 | this.network = options.network; |
||
39 | } else { |
||
40 | this.network = this.getBitcoinNetwork(this.settings.network, this.settings.testnet, this.settings.regtest); |
||
41 | } |
||
42 | |||
43 | backupData.walletVersion = backupData.walletVersion || 2; //default to version 2 wallets |
||
44 | |||
45 | var usePassword = false; |
||
46 | |||
47 | // validate backup data, cleanup input, and prepare seeds |
||
48 | if (!Array.isArray(backupData.blocktrailKeys)) { |
||
49 | throw new Error('blocktrail pub keys are required (must be type Array)'); |
||
50 | } |
||
51 | |||
52 | switch (backupData.walletVersion) { |
||
53 | case 1: |
||
54 | if (typeof backupData.primaryMnemonic === "undefined" || !backupData.primaryMnemonic) { |
||
55 | throw new Error('missing primary mnemonic for version 1 wallet'); |
||
56 | } |
||
57 | if (typeof backupData.backupMnemonic === "undefined" || !backupData.backupMnemonic) { |
||
58 | throw new Error('missing backup mnemonic for version 1 wallet'); |
||
59 | } |
||
60 | if (typeof backupData.primaryPassphrase === "undefined") { |
||
61 | throw new Error('missing primary passphrase for version 1 wallet'); |
||
62 | } |
||
63 | |||
64 | // cleanup copy paste errors from mnemonics |
||
65 | backupData.primaryMnemonic = backupData.primaryMnemonic.trim() |
||
66 | .replace(new RegExp("\r\n", 'g'), " ") |
||
67 | .replace(new RegExp("\n", 'g'), " ") |
||
68 | .replace(/\s+/g, " "); |
||
69 | backupData.backupMnemonic = backupData.backupMnemonic.trim() |
||
70 | .replace(new RegExp("\r\n", 'g'), " ") |
||
71 | .replace(new RegExp("\n", 'g'), " ") |
||
72 | .replace(/\s+/g, " "); |
||
73 | break; |
||
74 | |||
75 | case 2: |
||
76 | case 3: |
||
77 | if (typeof backupData.encryptedPrimaryMnemonic === "undefined" || !backupData.encryptedPrimaryMnemonic) { |
||
78 | throw new Error('missing encrypted primary seed for version 2 wallet'); |
||
79 | } |
||
80 | if (typeof backupData.backupMnemonic === "undefined" || (!backupData.backupMnemonic && backupData.backupMnemonic !== false)) { |
||
81 | throw new Error('missing backup seed for version 2 wallet'); |
||
82 | } |
||
83 | //can either recover with password and password encrypted secret, or with encrypted recovery secret and a decryption key |
||
84 | usePassword = typeof backupData.password !== "undefined" && backupData.password !== null; |
||
85 | if (usePassword) { |
||
86 | if (typeof backupData.passwordEncryptedSecretMnemonic === "undefined" || !backupData.passwordEncryptedSecretMnemonic) { |
||
87 | throw new Error('missing password encrypted secret for version 2 wallet'); |
||
88 | } |
||
89 | if (typeof backupData.password === "undefined") { |
||
90 | throw new Error('missing primary passphrase for version 2 wallet'); |
||
91 | } |
||
92 | } else { |
||
93 | if (typeof backupData.encryptedRecoverySecretMnemonic === "undefined" || !backupData.encryptedRecoverySecretMnemonic) { |
||
94 | throw new Error('missing encrypted recovery secret for version 2 wallet (recovery without password)'); |
||
95 | } |
||
96 | if (!backupData.recoverySecretDecryptionKey) { |
||
97 | throw new Error('missing recovery secret decryption key for version 2 wallet (recovery without password)'); |
||
98 | } |
||
99 | } |
||
100 | |||
101 | // cleanup copy paste errors from mnemonics |
||
102 | backupData.encryptedPrimaryMnemonic = backupData.encryptedPrimaryMnemonic.trim() |
||
103 | .replace(new RegExp("\r\n", 'g'), " ") |
||
104 | .replace(new RegExp("\n", 'g'), " ") |
||
105 | .replace(/\s+/g, " "); |
||
106 | backupData.backupMnemonic = (backupData.backupMnemonic || "").trim() |
||
107 | .replace(new RegExp("\r\n", 'g'), " ") |
||
108 | .replace(new RegExp("\n", 'g'), " ") |
||
109 | .replace(/\s+/g, " "); |
||
110 | if (backupData.recoverySecretDecryptionKey) { |
||
111 | backupData.recoverySecretDecryptionKey = backupData.recoverySecretDecryptionKey.trim() |
||
112 | .replace(new RegExp("\r\n", 'g'), " ") |
||
113 | .replace(new RegExp("\n", 'g'), " ") |
||
114 | .replace(/\s+/g, " "); |
||
115 | } |
||
116 | if (usePassword) { |
||
117 | backupData.passwordEncryptedSecretMnemonic = backupData.passwordEncryptedSecretMnemonic.trim() |
||
118 | .replace(new RegExp("\r\n", 'g'), " ").replace(new RegExp("\n", 'g'), " ").replace(/\s+/g, " "); |
||
119 | } else { |
||
120 | backupData.encryptedRecoverySecretMnemonic = backupData.encryptedRecoverySecretMnemonic.trim() |
||
121 | .replace(new RegExp("\r\n", 'g'), " ").replace(new RegExp("\n", 'g'), " ").replace(/\s+/g, " "); |
||
122 | } |
||
123 | |||
124 | break; |
||
125 | |||
126 | default: |
||
127 | throw new Error('Wrong version [' + backupData.walletVersion + ']'); |
||
128 | } |
||
129 | |||
130 | |||
131 | // create BIP32 HDNodes for the Blocktrail public keys |
||
132 | this.blocktrailPublicKeys = {}; |
||
133 | _.each(backupData.blocktrailKeys, function(blocktrailKey) { |
||
134 | self.blocktrailPublicKeys[blocktrailKey['keyIndex']] = bitcoin.HDNode.fromBase58(blocktrailKey['pubkey'], self.network); |
||
135 | }); |
||
136 | |||
137 | // convert the primary and backup mnemonics to seeds (using BIP39) |
||
138 | var primarySeed, backupSeed, secret; |
||
139 | switch (backupData.walletVersion) { |
||
140 | case 1: |
||
141 | primarySeed = bip39.mnemonicToSeed(backupData.primaryMnemonic, backupData.primaryPassphrase); |
||
142 | backupSeed = bip39.mnemonicToSeed(backupData.backupMnemonic, ""); |
||
143 | break; |
||
144 | |||
145 | case 2: |
||
146 | // convert mnemonics to hex (bip39) and then base64 for decryption |
||
147 | backupData.encryptedPrimaryMnemonic = blocktrail.convert(bip39.mnemonicToEntropy(backupData.encryptedPrimaryMnemonic), 'hex', 'base64'); |
||
148 | if (usePassword) { |
||
149 | backupData.passwordEncryptedSecretMnemonic = blocktrail.convert( |
||
150 | bip39.mnemonicToEntropy(backupData.passwordEncryptedSecretMnemonic), 'hex', 'base64'); |
||
151 | } else { |
||
152 | backupData.encryptedRecoverySecretMnemonic = blocktrail.convert( |
||
153 | bip39.mnemonicToEntropy(backupData.encryptedRecoverySecretMnemonic), 'hex', 'base64'); |
||
154 | } |
||
155 | |||
156 | // decrypt encryption secret |
||
157 | if (usePassword) { |
||
158 | secret = CryptoJS.AES.decrypt(backupData.passwordEncryptedSecretMnemonic, backupData.password).toString(CryptoJS.enc.Utf8); |
||
159 | } else { |
||
160 | secret = CryptoJS.AES.decrypt(backupData.encryptedRecoverySecretMnemonic, backupData.recoverySecretDecryptionKey).toString(CryptoJS.enc.Utf8); |
||
161 | } |
||
162 | |||
163 | if (!secret) { |
||
164 | throw new Error("Could not decrypt secret with " + (usePassword ? "password" : "decryption key")); |
||
165 | } |
||
166 | |||
167 | // now finally decrypt the primary seed and convert to buffer (along with backup seed) |
||
168 | primarySeed = new Buffer(CryptoJS.AES.decrypt(backupData.encryptedPrimaryMnemonic, secret).toString(CryptoJS.enc.Utf8), 'base64'); |
||
|
|||
169 | |||
170 | if (backupData.backupMnemonic) { |
||
171 | backupSeed = new Buffer(bip39.mnemonicToEntropy(backupData.backupMnemonic), 'hex'); |
||
172 | } |
||
173 | |||
174 | break; |
||
175 | |||
176 | case 3: |
||
177 | // convert mnemonics to hex (bip39) and then base64 for decryption |
||
178 | backupData.encryptedPrimaryMnemonic = EncryptionMnemonic.decode(backupData.encryptedPrimaryMnemonic); |
||
179 | if (usePassword) { |
||
180 | backupData.passwordEncryptedSecretMnemonic = EncryptionMnemonic.decode(backupData.passwordEncryptedSecretMnemonic); |
||
181 | } else { |
||
182 | backupData.encryptedRecoverySecretMnemonic = EncryptionMnemonic.decode(backupData.encryptedRecoverySecretMnemonic); |
||
183 | } |
||
184 | |||
185 | // decrypt encryption secret |
||
186 | if (usePassword) { |
||
187 | secret = Encryption.decrypt(backupData.passwordEncryptedSecretMnemonic, new Buffer(backupData.password)); |
||
188 | } else { |
||
189 | secret = Encryption.decrypt(backupData.encryptedRecoverySecretMnemonic, new Buffer(backupData.recoverySecretDecryptionKey, 'hex')); |
||
190 | } |
||
191 | |||
192 | if (!secret) { |
||
193 | throw new Error("Could not decrypt secret with " + (usePassword ? "password" : "decryption key")); |
||
194 | } |
||
195 | |||
196 | // now finally decrypt the primary seed and convert to buffer (along with backup seed) |
||
197 | primarySeed = Encryption.decrypt(backupData.encryptedPrimaryMnemonic, secret); |
||
198 | if (backupData.backupMnemonic) { |
||
199 | backupSeed = new Buffer(bip39.mnemonicToEntropy(backupData.backupMnemonic), 'hex'); |
||
200 | } |
||
201 | |||
202 | break; |
||
203 | |||
204 | default: |
||
205 | throw new Error('Wrong version [' + backupData.walletVersion + ']'); |
||
206 | } |
||
207 | |||
208 | // convert the primary and backup seeds to private keys (using BIP32) |
||
209 | this.primaryPrivateKey = bitcoin.HDNode.fromSeedBuffer(primarySeed, this.network); |
||
210 | |||
211 | if (backupSeed) { |
||
212 | this.backupPrivateKey = bitcoin.HDNode.fromSeedBuffer(backupSeed, this.network); |
||
213 | this.backupPublicKey = this.backupPrivateKey.neutered(); |
||
214 | } else { |
||
215 | this.backupPrivateKey = false; |
||
216 | this.backupPublicKey = bitcoin.HDNode.fromBase58(backupData.backupPublicKey, this.network); |
||
217 | } |
||
218 | |||
219 | if (this.settings.logging) { |
||
220 | console.log('using password method: ' + usePassword); |
||
221 | console.log("Primary Prv Key: " + this.primaryPrivateKey.toBase58()); |
||
222 | console.log("Primary Pub Key: " + this.primaryPrivateKey.neutered().toBase58()); |
||
223 | console.log("Backup Prv Key: " + (this.backupPrivateKey ? this.backupPrivateKey.toBase58() : null)); |
||
224 | console.log("Backup Pub Key: " + this.backupPublicKey.toBase58()); |
||
225 | } |
||
226 | }; |
||
227 | |||
741 |
This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.
To learn more about declaring variables in Javascript, see the MDN.